#include <sstream>
#include <algorithm>
#include <stdexcept>
#include <unordered_map>

#include "../../include/CLink/database/QueryBuilder.h"

namespace CLink {
namespace Db {

std::string operatorToString(CLink::Db::OperatorType op) {
    static const std::unordered_map<CLink::Db::OperatorType, std::string> opMap = {
        {CLink::Db::OperatorType::EQUAL, "="},
        {CLink::Db::OperatorType::NOT_EQUAL, "!="},
        {CLink::Db::OperatorType::LESS, "<"},
        {CLink::Db::OperatorType::LESS_EQUAL, "<="},
        {CLink::Db::OperatorType::GREATER, ">"},
        {CLink::Db::OperatorType::GREATER_EQUAL, ">="},
        {CLink::Db::OperatorType::LIKE, "LIKE"},
        {CLink::Db::OperatorType::NOT_LIKE, "NOT LIKE"},
        {CLink::Db::OperatorType::IS_NULL, "IS NULL"},
        {CLink::Db::OperatorType::IS_NOT_NULL, "IS NOT NULL"},
        {CLink::Db::OperatorType::IS_IN, "IN"},
        {CLink::Db::OperatorType::NOT_IN, "NOT IN"},
        {CLink::Db::OperatorType::BETWEEN, "BETWEEN"},
        {CLink::Db::OperatorType::NOT_BETWEEN, "NOT BETWEEN"}
    };
    return opMap.at(op);
}

QueryBuilder::QueryBuilder()
    : _type(QueryType::SELECT)
    , _distinct(false)
    , _limit(-1)
    , _offset(-1) {
}

QueryBuilder& QueryBuilder::select(const std::vector<std::string>& columns) {
    _type = QueryType::SELECT;
    _columns = columns;
    return *this;
}

QueryBuilder& QueryBuilder::select(const std::string& column) {
    _type = QueryType::SELECT;
    _columns.push_back(column);
    return *this;
}

QueryBuilder& QueryBuilder::selectRaw(const std::string& expression) {
    _type = QueryType::SELECT;
    _columns.push_back(expression);
    return *this;
}

QueryBuilder& QueryBuilder::distinct() {
    _distinct = true;
    return *this;
}

QueryBuilder& QueryBuilder::from(const std::string& table) {
    _table = table;
    return *this;
}

QueryBuilder& QueryBuilder::join(const std::string& table,
    const std::string& first,
    OperatorType op,
    const std::string& second,
    JoinType type) {
    JoinClause join;
    join.type = type;
    join.table = table;
    join.condition = first + " " + operatorToString(op) + " " + second;
    _joins.push_back(join);
    return *this;
}

QueryBuilder& QueryBuilder::leftJoin(const std::string& table,
    const std::string& first,
    OperatorType op,
    const std::string& second) {
    return join(table, first, op, second, JoinType::LEFT);
}

QueryBuilder& QueryBuilder::rightJoin(const std::string& table,
    const std::string& first,
    OperatorType op,
    const std::string& second) {
    return join(table, first, op, second, JoinType::RIGHT);
}

QueryBuilder& QueryBuilder::innerJoin(const std::string& table,
    const std::string& first,
    OperatorType op,
    const std::string& second) {
    return join(table, first, op, second, JoinType::INNER);
}

QueryBuilder& QueryBuilder::where(const std::string& column,
    OperatorType op,
    const std::string& value) {
    WhereClause where;
    where.type = _wheres.empty() ? WhereType::WHERE : WhereType::AND;
    where.column = column;
    where.op = operatorToString(op);
    where.value = value;
    where.raw = false;
    _wheres.push_back(where);
    return *this;
}

QueryBuilder& QueryBuilder::where(const std::string& column,
    const std::string& value) {
    return where(column, OperatorType::EQUAL, value);
}

QueryBuilder& QueryBuilder::whereRaw(const std::string& raw) {
    WhereClause where;
    where.type = _wheres.empty() ? WhereType::WHERE : WhereType::AND;
    where.column = raw;
    where.raw = true;
    _wheres.push_back(where);
    return *this;
}

QueryBuilder& QueryBuilder::orWhere(const std::string& column,
    OperatorType op,
    const std::string& value) {
    WhereClause where;
    where.type = WhereType::OR;
    where.column = column;
    where.op = operatorToString(op);
    where.value = value;
    where.raw = false;
    _wheres.push_back(where);
    return *this;
}

QueryBuilder& QueryBuilder::orWhere(const std::string& column,
    const std::string& value) {
    return orWhere(column, OperatorType::EQUAL, value);
}

QueryBuilder& QueryBuilder::orWhereRaw(const std::string& raw) {
    WhereClause where;
    where.type = WhereType::OR;
    where.column = raw;
    where.raw = true;
    _wheres.push_back(where);
    return *this;
}

QueryBuilder& QueryBuilder::whereIn(const std::string& column,
    const std::vector<std::string>& values) {
    if (values.empty()) {
        return *this;
    }

    std::ostringstream oss;
    oss << column << " IN (";
    for (size_t i = 0; i < values.size(); ++i) {
        if (i > 0) {
            oss << ", ";
        }
        oss << "'" << values[i] << "'";
    }
    oss << ")";

    return whereRaw(oss.str());
}

QueryBuilder& QueryBuilder::whereNotIn(const std::string& column,
    const std::vector<std::string>& values) {
    if (values.empty()) {
        return *this;
    }

    std::ostringstream oss;
    oss << column << " NOT IN (";
    for (size_t i = 0; i < values.size(); ++i) {
        if (i > 0) {
            oss << ", ";
        }
        oss << "'" << values[i] << "'";
    }
    oss << ")";

    return whereRaw(oss.str());
}

QueryBuilder& QueryBuilder::whereNull(const std::string& column) {
    return whereRaw(column + " IS NULL");
}

QueryBuilder& QueryBuilder::whereNotNull(const std::string& column) {
    return whereRaw(column + " IS NOT NULL");
}

QueryBuilder& QueryBuilder::whereBetween(const std::string& column,
    const std::string& min,
    const std::string& max) {
    std::ostringstream oss;
    oss << column << " BETWEEN '" << min << "' AND '" << max << "'";
    return whereRaw(oss.str());
}

QueryBuilder& QueryBuilder::whereNotBetween(const std::string& column,
    const std::string& min,
    const std::string& max) {
    std::ostringstream oss;
    oss << column << " NOT BETWEEN '" << min << "' AND '" << max << "'";
    return whereRaw(oss.str());
}

QueryBuilder& QueryBuilder::groupBy(const std::string& column) {
    _groups.push_back(column);
    return *this;
}

QueryBuilder& QueryBuilder::groupBy(const std::vector<std::string>& columns) {
    _groups.insert(_groups.end(), columns.begin(), columns.end());
    return *this;
}

QueryBuilder& QueryBuilder::having(const std::string& column,
    OperatorType op,
    const std::string& value) {
    WhereClause having;
    having.type = _havings.empty() ? WhereType::WHERE : WhereType::AND;
    having.column = column;
    having.op = operatorToString(op);
    having.value = value;
    having.raw = false;
    _havings.push_back(having);
    return *this;
}

QueryBuilder& QueryBuilder::orHaving(const std::string& column,
    OperatorType op,
    const std::string& value) {
    WhereClause having;
    having.type = WhereType::OR;
    having.column = column;
    having.op = operatorToString(op);
    having.value = value;
    having.raw = false;
    _havings.push_back(having);
    return *this;
}

QueryBuilder& QueryBuilder::orderBy(const std::string& column, bool ascending) {
    OrderClause order;
    order.column = column;
    order.ascending = ascending;
    _orders.push_back(order);
    return *this;
}

QueryBuilder& QueryBuilder::orderByDesc(const std::string& column) {
    return orderBy(column, false);
}

QueryBuilder& QueryBuilder::limit(int limit) {
    _limit = limit;
    return *this;
}

QueryBuilder& QueryBuilder::offset(int offset) {
    _offset = offset;
    return *this;
}

QueryBuilder& QueryBuilder::page(int page, int perPage) {
    _limit = perPage;
    _offset = (page - 1) * perPage;
    return *this;
}

QueryBuilder& QueryBuilder::insert(const std::map<std::string, std::string>& data) {
    _type = QueryType::INSERT;
    _insertData.clear();
    _insertData.push_back(data);
    return *this;
}

QueryBuilder& QueryBuilder::insert(const std::vector<std::map<std::string, std::string>>& data) {
    _type = QueryType::INSERT;
    _insertData = data;
    return *this;
}

QueryBuilder& QueryBuilder::update(const std::map<std::string, std::string>& data) {
    _type = QueryType::UPDATE;
    _updateData = data;
    return *this;
}

QueryBuilder& QueryBuilder::del() {
    _type = QueryType::DELETED;
    return *this;
}

std::string QueryBuilder::build() const {
    std::ostringstream sql;

    switch (_type) {
    case QueryType::SELECT:
        buildSelect(sql);
        break;
    case QueryType::INSERT:
        buildInsert(sql);
        break;
    case QueryType::UPDATE:
        buildUpdate(sql);
        break;
    case QueryType::DELETED:
        buildDelete(sql);
        break;
    }

    return sql.str();
}

void QueryBuilder::reset() {
    _type = QueryType::SELECT;
    _table.clear();
    _columns.clear();
    _distinct = false;
    _joins.clear();
    _wheres.clear();
    _groups.clear();
    _havings.clear();
    _orders.clear();
    _limit = -1;
    _offset = -1;
    _insertData.clear();
    _updateData.clear();
}

void QueryBuilder::buildSelect(std::ostringstream& sql) const {
    sql << "SELECT ";

    if (_distinct) {
        sql << "DISTINCT ";
    }

    if (_columns.empty()) {
        sql << "*";
    }
    else {
        for (size_t i = 0; i < _columns.size(); ++i) {
            if (i > 0) {
                sql << ", ";
            }
            sql << _columns[i];
        }
    }

    sql << " FROM " << _table;

    buildJoins(sql);
    buildWheres(sql);
    buildGroups(sql);
    buildHavings(sql);
    buildOrders(sql);

    if (_limit > 0) {
        sql << " LIMIT " << _limit;
    }

    if (_offset >= 0) {
        sql << " OFFSET " << _offset;
    }
}

void QueryBuilder::buildInsert(std::ostringstream& sql) const {
    if (_insertData.empty() || _table.empty()) 
    {
        throw std::runtime_error("insert data or table name cant not empty");
    }

    sql << "INSERT INTO " << _table << " (";

    // 获取所有列名
    std::vector<std::string> columns;
    for (const auto& [key, _] : _insertData[0]) {
        columns.push_back(key);
    }

    // 添加列名
    for (size_t i = 0; i < columns.size(); ++i) {
        if (i > 0) {
            sql << ", ";
        }
        sql << columns[i];
    }

    sql << ") VALUES ";

    // 添加值
    for (size_t i = 0; i < _insertData.size(); ++i) {
        if (i > 0) {
            sql << ", ";
        }

        sql << "(";
        for (size_t j = 0; j < columns.size(); ++j) {
            if (j > 0) {
                sql << ", ";
            }

            auto it = _insertData[i].find(columns[j]);
            if (it != _insertData[i].end()) {
                sql << "'" << it->second << "'";
            }
            else {
                sql << "NULL";
            }
        }
        sql << ")";
    }
}

void QueryBuilder::buildUpdate(std::ostringstream& sql) const {
    if (_updateData.empty() || _table.empty()) {
        throw std::runtime_error("update date or table name cant not empty");
    }

    sql << "UPDATE " << _table << " SET ";

    size_t i = 0;
    for (const auto& [key, value] : _updateData) {
        if (i > 0) {
            sql << ", ";
        }
        sql << key << " = '" << value << "'";
        ++i;
    }

    buildWheres(sql);
}

void QueryBuilder::buildDelete(std::ostringstream& sql) const {
    if (_table.empty()) {
        throw std::runtime_error("表名不能为空");
    }

    sql << "DELETE FROM " << _table;
    buildWheres(sql);
}

void QueryBuilder::buildJoins(std::ostringstream& sql) const {
    for (const auto& join : _joins) {
        switch (join.type) {
        case JoinType::INNER:
            sql << " INNER JOIN ";
            break;
        case JoinType::LEFT:
            sql << " LEFT JOIN ";
            break;
        case JoinType::RIGHT:
            sql << " RIGHT JOIN ";
            break;
        }

        sql << join.table << " ON " << join.condition;
    }
}

void QueryBuilder::buildWheres(std::ostringstream& sql) const {
    for (size_t i = 0; i < _wheres.size(); ++i) {
        const auto& where = _wheres[i];

        if (i == 0) {
            sql << " WHERE ";
        }
        else {
            switch (where.type) {
            case WhereType::AND:
                sql << " AND ";
                break;
            case WhereType::OR:
                sql << " OR ";
                break;
            default:
                break;
            }
        }

        if (where.raw) {
            sql << where.column;
        }
        else {
            sql << where.column << " " << where.op << " '" << where.value << "'";
        }
    }
}

void QueryBuilder::buildGroups(std::ostringstream& sql) const {
    if (!_groups.empty()) {
        sql << " GROUP BY ";
        for (size_t i = 0; i < _groups.size(); ++i) {
            if (i > 0) {
                sql << ", ";
            }
            sql << _groups[i];
        }
    }
}

void QueryBuilder::buildHavings(std::ostringstream& sql) const {
    for (size_t i = 0; i < _havings.size(); ++i) {
        const auto& having = _havings[i];

        if (i == 0) {
            sql << " HAVING ";
        }
        else {
            switch (having.type) {
            case WhereType::AND:
                sql << " AND ";
                break;
            case WhereType::OR:
                sql << " OR ";
                break;
            default:
                break;
            }
        }

        if (having.raw) {
            sql << having.column;
        }
        else {
            sql << having.column << " " << having.op << " '" << having.value << "'";
        }
    }
}

void QueryBuilder::buildOrders(std::ostringstream& sql) const {
    if (!_orders.empty()) {
        sql << " ORDER BY ";
        for (size_t i = 0; i < _orders.size(); ++i) {
            if (i > 0) {
                sql << ", ";
            }
            sql << _orders[i].column;
            if (!_orders[i].ascending) {
                sql << " DESC";
            }
        }
    }
}

} // namespace db
} // namespace CLink